package regexInput;
import java.util.ArrayList;

public class NFABuilder {
	private ArrayList<TransitionNode[]> states;
	private ArrayList<Node> identifiers;
	
	public NFABuilder(String[] identifier){
		states = new ArrayList<TransitionNode[]>();	
		identifiers = new  ArrayList<Node>();//init our table
		//fill the list of identifiers from our strings into our iden list
		String[] identifierList = new String[identifier.length+1];
		for (int i = 0; i < identifier.length;i++){
			identifierList[i] = identifier[i];
		}
		//adds epsilon to our list
		identifierList[identifierList.length-1] = "E";
		identifier = identifierList;
		//create the array holding the identifier values
		TransitionNode[] node = new TransitionNode[identifier.length];
		for (int i = 0; i < identifier.length; i++){
			//get the names of the identifiers and puts them in the array list
			node[i] = new TransitionNode(identifier[i],0,0);
			identifiers.add(new Node());
			identifiers.get(i).setName(identifier[i]);
		}
		//adds the identifiers as the first states in the list
		states.add(0, node);
		
	}
	
	public void buildNFA(String regEx){
		String s = "";
		int stateCounter = 1;
		int target = 0;
		int current = 0;
		//creates a new table that will become the nfa
		ArrayList<TransitionNode[]> table  = new ArrayList<TransitionNode[]>();
		//and empty state that can be added to the nfa whenever we need a new state
		TransitionNode[] newState = new TransitionNode[identifiers.size()];
		for (int i = 0; i< identifiers.size();i++){
			newState[i] = new TransitionNode();
		}
		
		//set up the starting state	, if this is an nfa that is adding to another nfa, then we do not need a start state
		if (states.size()<=1){
			states.add(newState);
		}
		//loop through every character in regEx
		for (int i = 0; i < regEx.length(); i++){
			s = "";
			if (regEx.charAt(i) == '$'){
				s = "$";
				i++;
				//keeps adding characters to 's' until we reach a breaking character. (getting the next identifier)
				while((regEx.charAt(i) != '$')&&(regEx.charAt(i) != '|')&&(regEx.charAt(i) != '*')&&(regEx.charAt(i) != '(')&&(regEx.charAt(i) != ')')){
					s+=regEx.charAt(i);
					i++;
					if (i>=regEx.length())
						break;
				}
				//moves i back 1 so when we start reading the string in the next interation it will include '$'
				if (i<regEx.length()){
					if (regEx.charAt(i)=='$')
						i--;
				}
				//create a new empty state that can be added to our nfa
				newState = new TransitionNode[identifiers.size()];
				for (int k = 0; k< identifiers.size();k++){
					newState[k] = new TransitionNode();
				}
				
				//creates a new state that we can transition into
				states.add(newState);	
				//find the identifier that will cause the transition and sets that to our new state
				states.get(stateCounter)[findIdentifier(s)].setNextState(states.size()-1);
				//because there is 1 mroe state in iterate the state counter
				stateCounter++;
			}
			//check for character following the identifier
			if (i<regEx.length()){
				//if it is 'or'ed then we remove that extra state that we added because our new identifiers will be pointing to
				//the same state as the previous
				if (regEx.charAt(i) == '|'){
					stateCounter--;
					states.remove(states.size()-1);
				}
				//if there is a * then we create epsilon transitions.
				else if (regEx.charAt(i) == '*'){
					//current used for "()"
					if (current>0){
						states.get(current)[findIdentifier("E")].setNextState(stateCounter);
						states.get(stateCounter)[findIdentifier("E")].setNextState(current);
					}
					else{
						states.get(stateCounter-1)[findIdentifier("E")].setNextState(stateCounter);
						states.get(stateCounter)[findIdentifier("E")].setNextState(stateCounter-1);
					}
				}
				else if (regEx.charAt(i) == '('){
					i++;
					//hard coded values
					String[] r = {"$UPPERCASE","$LETTER","$LOWERCASE"};
					//create a new table that will represent the NFA inside ()
					NFABuilder newTable = new NFABuilder(r);
					//gets the string inside () and builds an NFA from it
					newTable.buildNFA(getInnerRegEx(regEx,i));
					//System.out.println("new table: " + newTable.toString());
					//removes the state that contains the identifiers
					//newTable.getStates().remove(0);
					//////////
					int paranCounter = 1;
					while (paranCounter !=0){
						if (regEx.charAt(i) == '(')
							paranCounter++;
						if (regEx.charAt(i) == ')')
							paranCounter--;
						i++;
					}
					if (i<regEx.length()){
					if (regEx.charAt(i) == '*'){
						//points an epsilon past this section
						newTable.getStates().get(0)[findIdentifier("E")].setNextState(newTable.getStates().size());
						for (int j = 0; i < newTable.getStates().size();j++){
							states.add(newTable.getStates().get(j));
							
						}
						newState = new TransitionNode[identifiers.size()];
						for (int k = 0; k< identifiers.size();k++){
							newState[k] = new TransitionNode();
						}
						states.add(newState);
							
					}
					if (regEx.charAt(i) == '|'){
						i++;
						if(regEx.charAt(i) == '('){
							
							i++;
							NFABuilder tab = new NFABuilder(r);
							tab.buildNFA(getInnerRegEx(regEx,i));
							//tab.getStates().remove(0);
							System.out.println("new table: " + newTable.toString());
							System.out.println("tab: " + tab.toString());
							for (int j =0; j < tab.getStates().get(0).length;j++){
								System.out.println(tab.getStates().get(1)[j].getNextState());
								if (tab.getStates().get(1)[j].getNextState()>0)
									newTable.getStates().get(1)[j].setNextState(tab.getStates().get(0).length+tab.getStates().get(0)[j].getNextState()-1);
							}
							
							for (int j = 0; i < tab.getStates().size();j++){
								newTable.getStates().add(tab.getStates().get(j));
								
							}
							System.out.println("new table: " + newTable.toString());
						}
						if(regEx.charAt(i) == '$'){
						}
						
					}
					}
					//////////
					//sets the first state of our new NFA to the last state of our overall NFA
					for (int j =0; j < newTable.getStates().get(0).length;j++)
						if (newTable.getStates().get(0)[j].getNextState()>0)
							newTable.getStates().get(0)[j].setNextState(stateCounter+newTable.getStates().get(0)[j].getNextState()-1);
					//add the new NFA to the end of our overall NFA
					states.set(stateCounter, newTable.getStates().get(0));
					//offsets the new NFA's next states
					for (int j = 1; j < newTable.getStates().size()-1;j++){
						for (int k = 0; k < newTable.getStates().get(0).length;k++){
							if (newTable.getStates().get(j)[k].getNextState()>0)
								newTable.getStates().get(j)[k].setNextState(stateCounter+newTable.getStates().get(j)[k].getNextState()-1);
						}
						
						newTable.getStates().set(j, newTable.getStates().get(j));
						current = stateCounter;
						stateCounter++;
						states.add(newTable.getStates().get(j));
					}
					
				}
			}
		}
	}
	
	public void addNFA(ArrayList<TransitionNode[]> newNFA){
		TransitionNode[] newState = new TransitionNode[identifiers.size()];
		for (int i = 0; i< identifiers.size();i++){
			newState[i] = new TransitionNode();
		}
		int offset = states.size()-2;
		
		for (int i = 0; i < states.get(1).length; i++){
			if ((states.get(1)[i].getNextState() == 0)&&(newNFA.get(1)[i].getNextState()!=0))
				states.get(1)[i].setNextState(newNFA.get(1)[i].getNextState() + offset);
		}
		
		for (int i = 2; i < newNFA.size();i++){
			states.add(newState);
			newState = new TransitionNode[identifiers.size()];
			for (int k = 0; k< identifiers.size();k++){
				newState[k] = new TransitionNode();
			}
			for (int j = 0; j < states.get(states.size()-1).length; j++){
				if (newNFA.get(i)[j].getNextState()>0)
					states.get(states.size()-1)[j].setNextState(newNFA.get(i)[j].getNextState()+offset);
			}
		}
	}
	
	public ArrayList<TransitionNode[]> getStates(){
		return states;
	}
	
	public String getInnerRegEx(String regEx,int startingPoint){
		int openCounter = 1;
		String newString = "";
		for (int i = startingPoint; i< regEx.length();i++){
			if (regEx.charAt(i) == '(')
				openCounter++;
			else if (regEx.charAt(i) == ')')
				openCounter--;
			if (openCounter == 0)
				return newString;
			newString += regEx.charAt(i); 
		}
		return "";
		
	}
	
	public int findIdentifier(String s){
		for (int i = 0;i<identifiers.size();i++){
			if (identifiers.get(i).getName().equals(s)){
				return i;
			}
		}
		System.out.println("nothing found");
		System.out.println(s);
		return 0;
	}
	public String toString(){
		String s = "";
		for (int i = 0; i<states.size();i++){
			s += '\n' + "State" + i + ":\t";
			for (int j = 0; j < identifiers.size(); j++){
				if (i == 0)
					s += states.get(i)[j].getIndentifier() + "\t";
				else
					s += states.get(i)[j].getNextState() + "\t\t";
			}
		}
		return s;
	}
}
